/**
 * @(#)CodificadorEnBase64.java 29-6-08
 *
 * Copyright (c) 2005-2010 PsiqueWare S.A. de C.V. All Rights Reserved.
 * This software is the proprietary information PsiqueWare S.A. de C.V.
 * Use is subject to license terms.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * <br/>Usuario : AngelElectrico
 * <br/>Creaci&oacute;n: 29-6-08
 * <br/>
 * <br/>
 * <br/><t5>Cambios :</t5>
 * <br/><b>Fecha | Autor  |  Cambios</b>
 * <ul>
 * <li>29-6-08 | <a href = "mailto:AngelElectrico@PsiqueWare.com">Angel El&eacute;ctrico</a> | Creaci&oacute;n del Objeto.</li>
 *
 * </ul>
 */

package com.psiqueware.alheli.cripto;

import java.util.ArrayList;
import java.util.List;

/**
 * Convertidor a Base 64.
 * <br/>Paquete Original: com.psiqueware.alheli.cripto
 * <br/>Nombre  Original: CodificadorEnBase64
 * <br/><a href="CodificadorEnBase64.java.html"><i>Ver C&oacute;digo Fuente</i> </a>
 *
 * @author <a href="mailto:AngelElectrico@psiqueware.com">Angel El&eacute;ctrico</a>
 * @version Revision: 1.0 29-6-08
 */
public final class CodificadorEnBase64
{

    /**
     * Listado de Tokens que componen el alfabeto de  Base64
     */
    public static final List<String> alfabeto = new ArrayList<String>(64);

    static
    {
        alfabeto.add("A");
        alfabeto.add("B");
        alfabeto.add("C");
        alfabeto.add("D");
        alfabeto.add("E");
        alfabeto.add("F");
        alfabeto.add("G");
        alfabeto.add("H");
        alfabeto.add("I");
        alfabeto.add("J");
        alfabeto.add("K");
        alfabeto.add("L");
        alfabeto.add("M");
        alfabeto.add("N");
        alfabeto.add("O");
        alfabeto.add("P");
        alfabeto.add("Q");
        alfabeto.add("R");
        alfabeto.add("S");
        alfabeto.add("T");
        alfabeto.add("U");
        alfabeto.add("V");
        alfabeto.add("W");
        alfabeto.add("X");
        alfabeto.add("Y");
        alfabeto.add("Z");
        alfabeto.add("a");
        alfabeto.add("b");
        alfabeto.add("c");
        alfabeto.add("d");
        alfabeto.add("e");
        alfabeto.add("f");
        alfabeto.add("g");
        alfabeto.add("h");
        alfabeto.add("i");
        alfabeto.add("j");
        alfabeto.add("k");
        alfabeto.add("l");
        alfabeto.add("m");
        alfabeto.add("n");
        alfabeto.add("o");
        alfabeto.add("p");
        alfabeto.add("q");
        alfabeto.add("r");
        alfabeto.add("s");
        alfabeto.add("t");
        alfabeto.add("u");
        alfabeto.add("v");
        alfabeto.add("w");
        alfabeto.add("x");
        alfabeto.add("y");
        alfabeto.add("z");
        alfabeto.add("0");
        alfabeto.add("1");
        alfabeto.add("2");
        alfabeto.add("3");
        alfabeto.add("4");
        alfabeto.add("5");
        alfabeto.add("6");
        alfabeto.add("7");
        alfabeto.add("8");
        alfabeto.add("9");
        alfabeto.add("+");
        alfabeto.add("/");
    }

    public static final char BASE_64 = '=';

    public static final char SALTO_DE_LINEA = (char) 10;

    public static final char RETORNO_CARRO = (char) 13;

    public static final int MAXIMO_LINEA = 75;

    public CodificadorEnBase64()
    {
    }

    public static String codificar(String origen)
    {
        byte[] bytesOrigen = origen.getBytes();
        int triadaBytes = bytesOrigen.length % 3;
        StringBuffer codificacion = new StringBuffer();
        int desplazamientoBit = 7;
        int desplazamientoBit64 = 5;
        int lugarEnByte = 0;
        byte valorToken = (byte) 0;
        int longitudLinea = 0;
        while (lugarEnByte < bytesOrigen.length)
        {
            valorToken = (byte) ((byte) valorToken | (byte) ((bytesOrigen[lugarEnByte] & (1 << desplazamientoBit)) > 0 ? (1 << desplazamientoBit64) : (byte) 0));
            desplazamientoBit--;
            if (desplazamientoBit < 0)
            {
                desplazamientoBit = 7;
                lugarEnByte++;
            }
            desplazamientoBit64--;
            if (desplazamientoBit64 < 0)
            {
                desplazamientoBit64 = 5;
                codificacion.append((String) (alfabeto.get(valorToken)));
                valorToken = (byte) 0;
                longitudLinea++;
                if (longitudLinea > MAXIMO_LINEA)
                {
                    codificacion.append(RETORNO_CARRO);
                    codificacion.append(SALTO_DE_LINEA);
                    longitudLinea = 0;
                }
            }
        }
        if (desplazamientoBit64 != 5)
        {
            lugarEnByte--;
            for (int i = desplazamientoBit64; i >= 0; i--)
            {
                if (desplazamientoBit >= 0)
                {
                    valorToken = (byte) ((byte) valorToken | (byte) ((bytesOrigen[lugarEnByte] & (1 << desplazamientoBit)) > 0 ? (1 << i) : (byte) 0));
                }
                desplazamientoBit--;
            }
            codificacion.append((String) (alfabeto.get(valorToken)));
        }
        if (triadaBytes == 2)
        {
            codificacion.append(BASE_64);
        }
        else if (triadaBytes == 1)
        {
            codificacion.append(BASE_64);
            codificacion.append(BASE_64);
        }
        return codificacion.toString();
    }

    public static String decodificar(String origen)
    {
        StringBuffer decodificacion = new StringBuffer();
        int desplazamientoBit = 7;
        int desplazamientoB64 = 5;
        int lugarEnByte = 0;
        byte valorCaracter = (byte) 0;
        while (lugarEnByte < origen.length())
        {
            if (origen.charAt(lugarEnByte) == BASE_64)
            {
                // end processing when encountering special end-padding character
                break;
            }
            if (origen.charAt(lugarEnByte) == SALTO_DE_LINEA || origen.charAt(lugarEnByte) == RETORNO_CARRO)
            {
                // ignore standard line break characters
                lugarEnByte++;
                continue;
            }
            else
            {
                if (!alfabeto.contains(origen.substring(lugarEnByte, lugarEnByte + 1)))
                {
                    // ignore unknown characters (mostly implemented to deal with other line break character sequences)
                    lugarEnByte++;
                    continue;
                }
                else
                {
                    byte currentByte = (byte) (alfabeto.indexOf(origen.substring(lugarEnByte, lugarEnByte + 1)));
                    valorCaracter = (byte) ((byte) valorCaracter | (byte) ((currentByte & (1 << desplazamientoB64)) > 0 ? (1 << desplazamientoBit) : (byte) 0));
                    desplazamientoBit--;
                    if (desplazamientoBit < 0)
                    {
                        desplazamientoBit = 7;
                        decodificacion.append((char) valorCaracter);
                        valorCaracter = (byte) 0;
                    }
                    desplazamientoB64--;
                    if (desplazamientoB64 < 0)
                    {
                        desplazamientoB64 = 5;
                        lugarEnByte++;
                    }
                }
            }
        }
        return decodificacion.toString();
    }
}